
#include <stdio.h>
#include <string.h>
#include <iostream>

#include "VInfra/VDebug.h"
#include "JsonReader.h"

namespace Veda {

std::istream& operator>>( std::istream& sin, VJson& root )
{
	VJsonReader reader;
	if( !reader.parse(sin, root) )
		errorf("VJson 'opeartor>>' failed!\n");
	return sin;
}

VJsonReader::VJsonReader()
{
}

VJsonReader::~VJsonReader()
{
}

bool VJsonReader::read( const std::string& document, VJson& root)
{
	return parse(document, root);
}

bool VJsonReader::read( const char* beginDoc, const char* endDoc, VJson& root)
{
	return parse(beginDoc, endDoc, root);
}

bool VJsonReader::read( std::istream &is, VJson& root)
{
	return parse(is, root);
}

bool VJsonReader::parse( const std::string& document, VJson& root )
{
	document_ = document;
	const char* begin = document_.c_str();
	const char* end = begin + document_.length();
	return parse( begin, end, root );
}

bool VJsonReader::parse( std::istream& sin, VJson& root )
{
	std::string doc;
	std::getline(sin, doc, (char)EOF);
	return parse( doc, root );
}

bool VJsonReader::parse( const char* beginDoc, const char* endDoc, VJson& root )
{
	begin_ = (char*)beginDoc;
	end_ = (char*)endDoc;
	current_ = begin_;
	while ( !nodes_.empty() )
		nodes_.pop();
	nodes_.push( &root );

	bool successful = readValue();
	Token token;
	skipCommentTokens( token );
	return successful;
}

VJson& VJsonReader::currentValue()
{
	return *(nodes_.top());
}

char VJsonReader::getNextChar()
{
	if ( current_ == end_ )
		return 0;
	return *current_++;
}

void VJsonReader::skipSpaces()
{
	while ( current_ != end_ )
	{
		char c = *current_;
		if ( c == ' '  ||  c == '\t'  ||  c == '\r'  ||  c == '\n' )
			++current_;
		else
			break;
	}
}

void VJsonReader::skipCommentTokens( Token& token )
{
	do
	{
		readToken( token );
	}
	while ( token.type_ == tokenComment );
}

bool VJsonReader::match( const char* pattern, int patternLength )
{
	if ( end_ - current_ < patternLength )
		return false;
	int index = patternLength;
	while ( index-- )
		if ( current_[index] != pattern[index] )
			return false;
	current_ += patternLength;
	return true;
}

bool VJsonReader::readToken( Token& token )
{
	skipSpaces();
	token.start_ = current_;
	char c = getNextChar();
	bool ok = true;
	switch ( c )
	{
	case '{':
		token.type_ = tokenObjectBegin;
		break;
	case '}':
		token.type_ = tokenObjectEnd;
		break;
	case '[':
		token.type_ = tokenArrayBegin;
		break;
	case ']':
		token.type_ = tokenArrayEnd;
		break;
	case '"':
		token.type_ = tokenString;
		ok = readString();
	break;
		case '/':
		token.type_ = tokenComment;
		ok = readComment();
		break;
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	case '-':
		token.type_ = tokenNumber;
		readNumber();
		break;
	case 't':
		token.type_ = tokenTrue;
		ok = match( "rue", 3 );
		break;
	case 'f':
		token.type_ = tokenFalse;
		ok = match( "alse", 4 );
		break;
	case 'n':
		token.type_ = tokenNull;
		ok = match( "ull", 3 );
		break;
	case ',':
		token.type_ = tokenArraySeparator;
		break;
	case ':':
		token.type_ = tokenMemberSeparator;
		break;
	case 0:
		token.type_ = tokenEndOfStream;
		break;
	default:
		ok = false;
		break;
	}
	if ( !ok )
	{
		errorf("VJsonReader in trouble, some symbols unrecognized!\n");
		return false;
	}
	token.end_ = current_;
	return true;
}

bool VJsonReader::readComment()
{
	char c = getNextChar();
	bool successful = false;
	if ( c == '*' )
	{
		while ( current_ != end_ )
		{
			char c = getNextChar();
			if ( c == '*'  &&  *current_ == '/' )
				break;
		}
		successful = getNextChar() == '/';
	}
	else if ( c == '/' )
	{
		while ( current_ != end_ )
		{
			char c = getNextChar();
			if (  c == '\r'  ||  c == '\n' )
				break;
		}
		successful = true;
	}
	if ( !successful )
		return false;
	return true;
}

bool VJsonReader::readString()
{
	char c = 0;
	while ( current_ != end_ )
	{
		c = getNextChar();
		if ( c == '\\' )
			getNextChar();
		else if ( c == '"' )
			break;
	}
	return c == '"';
}

void VJsonReader::readNumber()
{
	while ( current_ != end_ )
	{
		if ( !(*current_ >= '0'  &&  *current_ <= '9')  && !(*current_=='.' || *current_=='e' || *current_=='E' || *current_=='+' || *current_=='-'))
			break;
		++current_;
	}
}

bool VJsonReader::readValue()
{
	Token token;
	skipCommentTokens( token );
	bool successful = true;

	switch ( token.type_ )
	{
	case tokenObjectBegin:
		successful = readObject( token );
		break;
	case tokenArrayBegin:
		successful = readArray( token );
		break;
	case tokenNumber:
		successful = decodeNumber( token );
		break;
	case tokenString:
		successful = decodeString( token );
		break;
	case tokenTrue:
		currentValue() = true;
		break;
	case tokenFalse:
		currentValue() = false;
		break;
	case tokenNull:
		currentValue() = VJson();
		break;
	default:
		warnf("VJsonReader a error field is ignored!\n");
		currentValue() = VJson();
		break;
	}

	return successful;
}

bool VJsonReader::readObject( Token& tokenStart )
{
	Token tokenName;
	std::string name;
	currentValue() = VJson( objectValue );
	while ( readToken( tokenName ) )
	{
		bool initialTokenOk = true;
		while ( tokenName.type_ == tokenComment  &&  initialTokenOk )
			initialTokenOk = readToken( tokenName );
		if  ( !initialTokenOk )
			break;
		if ( tokenName.type_ == tokenObjectEnd  &&  name.empty() )  // empty object
			return true;
		if ( tokenName.type_ != tokenString )
			break;

		name = "";
		if ( !decodeString( tokenName, name ) )
			return false;

		Token colon;
		if ( !readToken( colon ) ||  colon.type_ != tokenMemberSeparator )
		{
			errorf("VJsonReader in trouble, missing ':' after object member name!\n");
			return false;
		}
		VJson &value = currentValue()[ name ];
		nodes_.push( &value );
		bool ok = readValue();
		nodes_.pop();
		if ( !ok )
			return false;

		Token comma;
		if ( !readToken( comma )
			|| ( comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && comma.type_ != tokenComment ) )
		{
			errorf("VJsonReader in trouble, missing ',' or '}' in object declaration!\n");
			return false;
		}
		bool finalizeTokenOk = true;
		while ( comma.type_ == tokenComment && finalizeTokenOk )
			finalizeTokenOk = readToken( comma );
		if ( comma.type_ == tokenObjectEnd )
			return true;
	}
	errorf("VJsonReader in trouble, missing '}' or object member name!\n");
	return false;
}

bool VJsonReader::readArray( Token& tokenStart )
{
	currentValue() = VJson( arrayValue );
	skipSpaces();
	if ( *current_ == ']' ) // empty array
	{
		Token endArray;
		readToken( endArray );
		return true;
	}
	int index = 0;
	while ( true )
	{
		VJson& value = currentValue()[ index++ ];
		nodes_.push( &value );
		bool ok = readValue();
		nodes_.pop();
		if ( !ok ) // error already set
		{
			return false;
		}

		Token token;
		// Accept Comment after last item in the array.
		ok = readToken( token );
		while ( token.type_ == tokenComment  &&  ok )
		{
			ok = readToken( token );
		}
		bool badTokenType = ( token.type_ == tokenArraySeparator && token.type_ == tokenArrayEnd );
		if ( !ok  ||  badTokenType )
		{
			errorf("VJsonReader in trouble, missing ',' or ']' in array declaration!\n");
			return false;
		}
		if ( token.type_ == tokenArrayEnd )
			break;
	}
	return true;
}

bool VJsonReader::decodeNumber( Token& token )
{
	bool isDouble = false;
	for ( char* inspect = token.start_; inspect != token.end_; ++inspect )
	{
		isDouble = (isDouble  
					|| (*inspect=='.' || *inspect=='e' || *inspect=='E' || *inspect=='+' )
					|| ( *inspect == '-'  &&  inspect != token.start_ ));
	}
	if ( isDouble )
		return decodeDouble( token );
	char* current = token.start_;
	bool isNegative = *current == '-';
	if ( isNegative )
		++current;
	uint threshold = (isNegative ? uint(-VJson::minInt) : VJson::maxUInt)/10;
	uint value = 0;
	while ( current < token.end_ )
	{
		char c = *current++;
		if ( c < '0'  ||  c > '9' )
		{
			errorf("VJsonReader in trouble, %s is not a number!\n", std::string(token.start_, token.end_).c_str());
			return false;
		}
		if ( value >= threshold )
			return decodeDouble( token );
		value = value * 10 + uint(c - '0');
	}
	if ( isNegative )
		currentValue() = -int( value );
	else if ( value <= uint(VJson::maxInt) )
		currentValue() = int( value );
	else
		currentValue() = value;
	return true;
}

bool VJsonReader::decodeDouble( Token& token )
{
	double value = 0;
	const int bufferSize = 32;
	int count;
	int length = int(token.end_ - token.start_);
	if ( length <= bufferSize )
	{
		char buffer[bufferSize];
		memcpy( buffer, token.start_, length );
		buffer[length] = 0;
		count = sscanf( buffer, "%lf", &value );
	}
	else
	{
		std::string buffer( token.start_, token.end_ );
		count = sscanf( buffer.c_str(), "%lf", &value );
	}

	if ( count != 1 )
	{
		errorf("VJsonReader in trouble, %s is not a number!\n",std::string( token.start_, token.end_ ).c_str());
		return false;
	}
	currentValue() = value;
	return true;
}

bool VJsonReader::decodeString( Token &token )
{
	std::string decoded;
	if ( !decodeString( token, decoded ) )
		return false;
	currentValue() = decoded;
	return true;
}

bool VJsonReader::decodeString( Token &token, std::string &decoded )
{
	decoded.reserve( token.end_ - token.start_ - 2 );
	char* current = token.start_ + 1; // skip '"'
	char* end = token.end_ - 1;      // do not include '"'
	while ( current != end )
	{
		char c = *current++;
		if ( c == '"' )
			break;
		else if ( c == '\\' )
		{
			if ( current == end )
			{
				errorf("VJsonReader in trouble, empty escape sequence in string!\n");
				return false; 
			}
			char escape = *current++;
			switch ( escape )
			{
			case '"':
				decoded += '"';
				break;
			case '/':
				decoded += '/';
				break;
			case '\\':
				decoded += '\\';
				break;
			case 'b':
				decoded += '\b';
				break;
			case 'f':
				decoded += '\f';
				break;
			case 'n':
				decoded += '\n';
				break;
			case 'r':
				decoded += '\r';
				break;
			case 't':
				decoded += '\t';
				break;
			case 'u':
				{
					uint unicode;
					if ( !decodeUnicodeCodePoint( token, current, end, unicode ) )
					{
						errorf("VJsonReader in trouble, bad UnicodeCode in string!\n");
						return false;
					}
					decoded += codePointToUTF8(unicode);
				}
				break;
			default:
				errorf("VJsonReader in trouble, bad escape sequence in string!\n");
				return false; 
			}
		}
		else
		{
			decoded += c;
		}
	}
	return true;
}

bool VJsonReader::decodeUnicodeCodePoint( Token& token, char*& current, char* end, uint& unicode )
{
	if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) )
		return false;
	if (unicode >= 0xD800 && unicode <= 0xDBFF)
	{
		// surrogate pairs
		if (end - current < 6)
			return false; 
		uint surrogatePair;
		if (*(current++) == '\\' && *(current++)== 'u')
		{
			if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair ))
				unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
			else
				return false;
		} 
		else
			return false; 
	}
	return true;
}

bool VJsonReader::decodeUnicodeEscapeSequence( Token& token, char*& current, char* end, uint& unicode )
{
	if ( end - current < 4 )
		return false; 
	unicode = 0;
	for ( int index =0; index < 4; ++index )
	{
		char c = *current++;
		unicode *= 16;
		if ( c >= '0'  &&  c <= '9' )
			unicode += c - '0';
		else if ( c >= 'a'  &&  c <= 'f' )
			unicode += c - 'a' + 10;
		else if ( c >= 'A'  &&  c <= 'F' )
			unicode += c - 'A' + 10;
		else
			return false; 
	}
	return true;
}


}

